using System;
using System.Collections.Generic;
using System.Linq;

namespace BOL.Maths
{
    /// <summary>
    /// Represents a VectorDouble class.
    /// </summary>
    public class VectorDouble : Vector<double>
    {
        #region Constructors

        public VectorDouble(IEnumerable<double> elements, bool isColumnVector = false) : base(elements, isColumnVector) { }

        public VectorDouble(int length, bool isColumnVector = false) : base(length, isColumnVector) { }

        #endregion

        #region ICloneable implementation

        public new VectorDouble Clone()
        {
            return new VectorDouble(Elements.Select(x => x).ToArray(), IsColumnVector);
        }

        #endregion

        #region Operators implementation

        public static VectorDouble operator +(VectorDouble u, VectorDouble v)
        {
            return new VectorDouble(u.Zip(v, (i, j) => i + j));
        }

        public static VectorDouble operator +(VectorDouble v, double a)
        {
            return new VectorDouble(v.Select(i => i + a));
        }

        public static VectorDouble operator +(double a, VectorDouble v)
        {
            return v + a;
        }

        public static VectorDouble operator -(VectorDouble v)
        {
            return new VectorDouble(v.Select(i => -i));
        }

        public static VectorDouble operator -(VectorDouble u, VectorDouble v)
        {
            return new VectorDouble(u.Zip(v, (i, j) => i - j));
        }

        public static VectorDouble operator -(VectorDouble v, double a)
        {
            return new VectorDouble(v.Select(i => i - a));
        }

        public static VectorDouble operator -(double a, VectorDouble v)
        {
            return new VectorDouble(v.Select(i => a - i));
        }

        public static VectorDouble operator *(VectorDouble u, VectorDouble v)
        {
            return new VectorDouble(u.Zip(v, (i, j) => i * j));
        }

        public static VectorDouble operator *(VectorDouble v, double a)
        {
            return new VectorDouble(v.Select(i => i * a));
        }

        public static VectorDouble operator *(double a, VectorDouble v)
        {
            return v * a;
        }

        public static VectorDouble operator /(VectorDouble u, VectorDouble v)
        {
            return new VectorDouble(u.Zip(v, (i, j) => i / j));
        }

        public static VectorDouble operator /(VectorDouble v, double a)
        {
            if (a.Equals(0))
                throw new DivideByZeroException();

            return new VectorDouble(v.Select(i => i / a));
        }

        public static VectorDouble operator /(double a, VectorDouble v)
        {
            return new VectorDouble(v.Select(i => a / i));
        }

        #endregion

        #region Object overriden

        public override string ToString()
        {
            return String.Format("{{{0}}}", String.Join(", ", Elements));
        }

        #endregion
    }
}
